﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using ApplicationLayer;
using Blokus.Controls;

#if (DEBUG)
using NUnit.Framework;
using Blokus.Controls;
#endif

namespace Blokus.Scenes
{
#if (DEBUG)
    [TestFixture]
#endif

    public class ScenePlaying : Scene
    {
        public Color[] playerColors = new Color[8];



        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        private SpriteFont menuFont;

        private List<string> menuItems = new List<string>();

        private TextBox btnForfeit;
        private KeyboardState keyPrevState;
        private GamePadState gpPrevState;
        private MouseState mousePrevState;
        private Texture2D texBG, texGrid, texSquare, texPointer, texPixel, texGamesquare, texStar;
        private float aspectRatio;
        private RenderTarget2D backgroundRenderTarget;
        GameDesign gd = new GameDesign();
        ApplicationLayer.Game myGame = new ApplicationLayer.Game();
        private int pieceSelected = -1;
        private double totalGridSize;
        private double totalWidth;
        private double totalHeight;
        private double totalPanelSize;
        private double cellWidth;
        private double cellHeight;
        private int totalPieces;
        private int pieceCount;
        private double pieceHeight;
        private double pieceLeft;
        private double pieceLeft1;
        private double pieceLeft2;
        private double pieceSize;
        private double gameGridSize;
        private double gameGridSizeHeight;
        private double gameGridLeft;
        private double gameGridTop;
        private double gameCellWidth;
        private double gameCellHeight;
        private Blokus blokusGame;
        private double turnTimer = 0;
        private double daemonTimer = 0;
        private double turnTimerStart = 0;
        private double daemonTimerStart = 0;
        public static GameDesign newGameDesign;
        public static List<Player> newPlayers;
        public double scrollAvail = 0;

        public ScenePlaying(Blokus game)
        {
            blokusGame = game;
            myGame = new ApplicationLayer.Game();
            newPlayers = new List<Player>();
            for (int ct = 0; ct < 4; ct ++)
                newPlayers.Add(new Player());
            newGameDesign = new GameDesign();
            newGameDesign.createOriginalBlokus();
            myGame.setup(newGameDesign, newPlayers);
            playerColors[0] = Color.Blue;
            playerColors[1] = Color.Red;
            playerColors[2] = Color.Yellow;
            playerColors[3] = Color.Green;
            playerColors[4] = Color.Purple;
            playerColors[5] = Color.Orange;
            playerColors[6] = Color.Brown;
            playerColors[7] = Color.Pink;
        }
        public void newGame()
        {
            myGame = new ApplicationLayer.Game();
            gd = newGameDesign;
            myGame.setup(gd, newPlayers);
            daemonTimer = myGame.daemonTimer;
            daemonTimerStart = myGame.daemonTimer;
            turnTimer = myGame.playerTurnTimeLimit;
            turnTimerStart = myGame.playerTurnTimeLimit;
            setupScene();
            scrollAvail = 0;
            pieceSelected = -1;
        }

        public void setupScene()
        {
            //initialize all graphical measures of width/height and position
            totalGridSize = 800;
            totalWidth = 1280;
            totalHeight = 800;
            totalPanelSize = totalWidth - totalGridSize;
            cellWidth = totalGridSize / myGame.board.numColumns;
            cellHeight = totalHeight / myGame.board.numRows;
            pieceCount = 0;
            totalPieces = myGame.players[myGame.currentTurn].getNumberOfPiecesAvailable();
            pieceHeight = totalHeight / Math.Ceiling(totalPieces / 2.0);//totalHeight / totalPieces;
            pieceLeft = (totalPanelSize - pieceHeight) / 2;
            pieceLeft1 = (totalPanelSize / 2 - pieceHeight) / 2;
            pieceLeft2 = pieceLeft1 + totalPanelSize / 2;
            pieceSize = pieceHeight / 5;

            gameGridSize = 800 - 60;
            gameGridSizeHeight = gameGridSize;
            gameGridLeft = totalPanelSize + 30;
            gameGridTop = 30;
            gameCellWidth = gameGridSize / myGame.board.numColumns;
            gameCellHeight = gameGridSize / myGame.board.numRows;
        }

        public override void LoadContent(ContentManager content, GraphicsDeviceManager graphics)
        {
            this.graphics = graphics;
            //load font
            menuFont = content.Load<SpriteFont>("gamefont");
            backgroundRenderTarget = new RenderTarget2D(graphics.GraphicsDevice, 1280, 800);
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);

            texBG = content.Load<Texture2D>("Textures\\background");
            texGrid = content.Load<Texture2D>("Textures\\Grid");
            texSquare = content.Load<Texture2D>("Textures\\Square");
            texPointer = content.Load<Texture2D>("Textures\\Pointer");
            texPixel = content.Load<Texture2D>("Textures\\pixel");
            texGamesquare = content.Load<Texture2D>("Textures\\gamesquare");
            texStar = content.Load<Texture2D>("Textures\\star");
            aspectRatio = graphics.GraphicsDevice.Viewport.AspectRatio;
            btnForfeit = new TextBox("Forfeit", false, new Rectangle(280, 130, (int)menuFont.MeasureString("Forfeit").X + 10, (int)menuFont.MeasureString("Forfeit").Y + 10));
            btnForfeit.isFocusable = false;
            btnForfeit.spriteBatch = spriteBatch;
            btnForfeit.GameFont = menuFont;
            btnForfeit.colorOnMouseOver = true;
            btnForfeit.backgroundTexture = content.Load<Texture2D>("Textures\\rectangle");


        }

        private void updateTurnTimer(GameTime gameTime)
        {
            turnTimer -= gameTime.ElapsedGameTime.TotalSeconds;
            if (turnTimer <= 0)
            {
                myGame.nextTurn();
                turnTimer += turnTimerStart;
            }
        }

        private void updateDaemonTimer(GameTime gameTime)
        {
            daemonTimer -= gameTime.ElapsedGameTime.TotalSeconds;
            if (daemonTimer <= 0)
            {
                myGame.daemonAttacks();
                daemonTimer += daemonTimerStart;
            }
        }

        private void updateScrollAvail(GameTime gameTime)
        {
            int availSize = (int)totalPanelSize / 5;
            int availCell = availSize / 6;
            double scrollAvailMax = Math.Ceiling(myGame.players[myGame.currentTurn].getNumberOfPiecesAvailable() / 4.0) * availSize - availSize * 6;
            if (scrollAvailMax <= 0)
                return;
            else
            {
                int x = Mouse.GetState().X;
                int y = Mouse.GetState().Y;
                if (x >= availSize * 0.5 && x <= availSize * 5.5)
                {
                    if (y >= 200 && y <= 200 + availSize * 0.5 && scrollAvail > 0)
                    {
                        scrollAvail -= availSize*3*gameTime.ElapsedGameTime.TotalSeconds;
                        if (scrollAvail < 0)
                            scrollAvail = 0;
                    }
                    else if (y >= 200 + availSize * 5.5 && y <= 200 + availSize * 6 && scrollAvail < scrollAvailMax)
                    {
                        scrollAvail += availSize * 3 * gameTime.ElapsedGameTime.TotalSeconds;
                        if (scrollAvail > scrollAvailMax)
                            scrollAvail = scrollAvailMax;
                    }
                }
            }
        }

        public override Blokus.GameState Update(GameTime gameTime)
        {
            setupScene();
            //get keys
            GamePadState currGPState = GamePad.GetState(PlayerIndex.One);
            bool kUp = Keyboard.GetState().IsKeyDown(Keys.Up) && !keyPrevState.IsKeyDown(Keys.Up)
                || currGPState.IsButtonDown(Buttons.DPadUp) && !gpPrevState.IsButtonDown(Buttons.DPadUp);
            bool kDown = Keyboard.GetState().IsKeyDown(Keys.Down) && !keyPrevState.IsKeyDown(Keys.Down)
                || currGPState.IsButtonDown(Buttons.DPadDown) && !gpPrevState.IsButtonDown(Buttons.DPadDown);
            bool kLeft = Keyboard.GetState().IsKeyDown(Keys.Left) && !keyPrevState.IsKeyDown(Keys.Left)
                || currGPState.IsButtonDown(Buttons.DPadLeft) && !gpPrevState.IsButtonDown(Buttons.DPadLeft);
            bool kRight = Keyboard.GetState().IsKeyDown(Keys.Right) && !keyPrevState.IsKeyDown(Keys.Right)
                || currGPState.IsButtonDown(Buttons.DPadRight) && !gpPrevState.IsButtonDown(Buttons.DPadRight);
            bool kEnter = Keyboard.GetState().IsKeyDown(Keys.Enter) && !keyPrevState.IsKeyDown(Keys.Enter)
                || currGPState.IsButtonDown(Buttons.A) && !gpPrevState.IsButtonDown(Buttons.A);
            bool kEsc = Keyboard.GetState().IsKeyDown(Keys.Escape) && !keyPrevState.IsKeyDown(Keys.Escape)
                || Keyboard.GetState().IsKeyDown(Keys.Q) && !keyPrevState.IsKeyDown(Keys.Q);



            //mouse checking grab pieces
            int pieceCount = 0;
            int totalPieces = myGame.players[myGame.currentTurn].getNumberOfPiecesAvailable();
            //check mouse with each available piece
            /*if (Mouse.GetState().LeftButton == ButtonState.Pressed && Mouse.GetState().X < totalPanelSize)
            {
                for (int ct = 0; ct < myGame.players[myGame.currentTurn].pieces.Count; ct++)
                {
                    MyPiece piece = myGame.players[myGame.currentTurn].pieces[ct];
                    if (piece.isAvailable)
                    {
                        int x, y;
                        if (pieceCount < Math.Ceiling(totalPieces / 2.0))
                        {
                            x = (int)pieceLeft1;
                            y = (int)(pieceCount * pieceHeight);
                        }
                        else
                        {
                            x = (int)pieceLeft2;
                            y = (int)(Math.Floor(pieceCount - totalPieces / 2.0) * pieceHeight);
                        }
                        if (Mouse.GetState().X >= x && Mouse.GetState().X <= x + (int)pieceHeight &&
                            Mouse.GetState().Y >= y && Mouse.GetState().Y <= y + (int)pieceHeight)
                        {
                            pieceSelected = ct;
                            break;
                        }

                        pieceCount++;
                    }
                }
            }*/
            if (Mouse.GetState().LeftButton == ButtonState.Pressed && Mouse.GetState().X < totalPanelSize)
            {
                for (int ct = 0; ct < myGame.players[myGame.currentTurn].pieces.Count; ct++)
                {
                    MyPiece piece = myGame.players[myGame.currentTurn].pieces[ct];
                    if (piece.isAvailable)
                    {
                        int col = pieceCount % 4;
                        int rw = pieceCount / 4;
                        int startTop = 200;
                        int availSize = (int)totalPanelSize / 5;
                        int availCell = availSize / 6;
                        int startLeft = availSize / 2;
                        int left = startLeft + availSize * col;
                        int top = startTop + availSize * rw;
                        if (Mouse.GetState().X >= left && Mouse.GetState().X <= left + availSize &&
                            Mouse.GetState().Y >= top - scrollAvail && Mouse.GetState().Y <= top + availSize - scrollAvail)
                        {
                            pieceSelected = ct;
                            break;
                        }
                        pieceCount++;
                    }
                }
            }
            //place piece
            else if (Mouse.GetState().LeftButton == ButtonState.Pressed && pieceSelected >= 0)
            {
                int mouseX = Mouse.GetState().X, mouseY = Mouse.GetState().Y;
                int newX = (int)Math.Round((Mouse.GetState().X - gameCellWidth * 2.5 - gameGridLeft) / (double)gameCellWidth);
                int newY = (int)Math.Round((Mouse.GetState().Y - gameCellHeight * 2.5 - gameGridTop) / (double)gameCellHeight);
                if (myGame.PlacePiece(newX, newY, pieceSelected))
                    pieceSelected = -1;
            }

            if (Mouse.GetState().RightButton == ButtonState.Pressed && pieceSelected >= 0 && Mouse.GetState().X > totalPanelSize && mousePrevState.RightButton != ButtonState.Pressed)
            {
                rotateSelectedPiece();
            }
            if (turnTimerStart > 0)
            {
                updateTurnTimer(gameTime);
            }
            if (daemonTimerStart > 0)
            {
                updateDaemonTimer(gameTime);
            }
            updateScrollAvail(gameTime);
            //check for menu item selected
            //reset previous keyboard/mouse state
            keyPrevState = Keyboard.GetState();
            gpPrevState = GamePad.GetState(PlayerIndex.One);
            mousePrevState = Mouse.GetState();

            if (btnForfeit.Update(gameTime, Mouse.GetState()))
            {
                myGame.players[myGame.currentTurn].gameover = true;
                bool allGameover = true;
                foreach (Player p in myGame.players)
                {
                    if (!p.gameover)
                    {
                        allGameover = false;
                        break;
                    }
                }
                if (!allGameover)
                    myGame.nextTurn();
                else
                    return Blokus.GameState.MENU;
            }
            if (kEsc)
            {
                blokusGame.IsMouseVisible = false;
                return Blokus.GameState.MENU;
            }
            return Blokus.GameState.PLAYING;
        }


        public void rotateSelectedPiece()
        {
            MyPiece myPiece = myGame.players[myGame.currentTurn].pieces[pieceSelected];
            if (myPiece.rotateStage == 3 || myPiece.rotateStage == 7)
                flipVertical();
            else
                rotateClockwise();
            myPiece.rotateStage++;
            if (myPiece.rotateStage > 7)
                myPiece.rotateStage = 0;
        }

        public void rotateClockwise()
        {
            MyPiece myPiece = myGame.players[myGame.currentTurn].pieces[pieceSelected];
            MyPiece temp = new MyPiece();
            foreach (Point p in myPiece.points)
                temp.points.Add(new Point(4 - p.Y, p.X));
            myPiece.points.Clear();
            foreach (Point p in temp.points)
                myPiece.points.Add(new Point(p.X, p.Y));
        }

        public void flipVertical()
        {
            MyPiece myPiece = myGame.players[myGame.currentTurn].pieces[pieceSelected];
            MyPiece temp = new MyPiece();
            foreach (Point p in myPiece.points)
                temp.points.Add(new Point(p.X, 4 - p.Y));
            myPiece.points.Clear();
            foreach (Point p in temp.points)
                myPiece.points.Add(new Point(p.X, p.Y));
        }

        public override void Draw(GameTime gameTime)
        {
            setupScene();
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
            //graphics.GraphicsDevice.SetRenderTarget(backgroundRenderTarget);
            spriteBatch.Begin();
            //draw background
            //spriteBatch.Draw(texBG, new Rectangle(0, 0, 1280, 800), Color.White);
            spriteBatch.Draw(texPixel, new Rectangle(0, 0, 1280, 800), colorMiddle);

            //draw game grid on right half of screen
            DrawGameGrid();
            DrawGameGridBorder();

            //draw selectable/playable pieces of current player's turn on left half of screen
            DrawAvailablePieces();

            //draw info in topleft
            DrawWords();

            //draw cursor
            DrawMouseCursor();


            spriteBatch.End();

        }

        public void DrawWords()
        {
            if (turnTimerStart > 0)
                spriteBatch.DrawString(menuFont, "Time Remaining: " + (int)Math.Ceiling(turnTimer), new Vector2(50, 50), Color.Black);
            spriteBatch.DrawString(menuFont, myGame.players[myGame.currentTurn].name, new Vector2(50, 130), Color.Black);
            btnForfeit.Draw();
        }

        public void DrawGameGridBorder()
        {
            int border = 15;
            int left = (int)Math.Round(gameGridLeft - border);
            int width = (int)Math.Round(gameGridSize + 2 * border);
            int top = 15;
            int height = (int)gameGridSizeHeight + 2 * border;
            DrawSquare(left, top, border, width, height, colorDark);
        }

        public void DrawSquare(int left, int top, int border, int width, int height, Color color)
        {
            spriteBatch.Draw(texPixel, new Rectangle(left, top, width, border), color);
            spriteBatch.Draw(texPixel, new Rectangle(left + width - border, top, border, height), color);
            spriteBatch.Draw(texPixel, new Rectangle(left, top + height - border, width, border), color);
            spriteBatch.Draw(texPixel, new Rectangle(left, top, border, height), color);
        }

        public void DrawSquareWithFill(int left, int top, int border, int width, int height, Color borderColor, Color fillColor)
        {
            spriteBatch.Draw(texPixel, new Rectangle(left + border, top + border, width - 2 * border, height - 2 * border), fillColor);
            DrawSquare(left, top, border, width, height, borderColor);
        }

        public void DrawGameGrid()
        {
            //draw game grid with pieces -- loop through all cells
            for (int rw = 0; rw < myGame.board.numRows; rw++)
            {
                for (int col = 0; col < myGame.board.numColumns; col++)
                {
                    Color color = Color.White;
                    //draw grid
                    if (myGame.boardMap[rw, col] >= 0) // if space on board is occupied, then draw player piece in cell
                        color = myGame.players[myGame.boardMap[rw, col]].color;
                    spriteBatch.Draw(texGamesquare, new Rectangle((int)(gameGridLeft + col * gameCellWidth), (int)(gameGridTop + rw * gameCellHeight), (int)gameCellWidth, (int)gameCellHeight), new Rectangle(0, 0, 32, 32), color);
                }
            }
            foreach (Point p in myGame.board.startingCells)
            {
                if (myGame.boardMap[p.Y, p.X] < 0)
                    spriteBatch.Draw(texStar, new Rectangle((int)(gameGridLeft + p.X * gameCellWidth), (int)(gameGridTop + p.Y * gameCellHeight), (int)gameCellWidth, (int)gameCellHeight), new Rectangle(0, 0, 32, 32), Color.White);
            }
            foreach (Point p in myGame.board.unplayable)
                spriteBatch.Draw(texGamesquare, new Rectangle((int)(gameGridLeft + p.X * gameCellWidth), (int)(gameGridTop + p.Y * gameCellHeight), (int)gameCellWidth, (int)gameCellHeight), new Rectangle(0, 0, 32, 32), Color.Black);
        }

        public void DrawAvailablePieces()
        {
            /*//draw available pieces
            for (int ct = 0; ct < myGame.players[myGame.currentTurn].pieces.Count; ct++)
            {
                MyPiece piece = myGame.players[myGame.currentTurn].pieces[ct];
                if (piece.isAvailable) //only draw if available
                {
                    foreach (Point p in piece.points)
                    {
                        if (pieceCount < Math.Ceiling(totalPieces / 2.0)) // draw 1st half of pieces in left column
                            spriteBatch.Draw(texSquare, new Rectangle((int)(pieceLeft1 + p.X * pieceSize), (int)(pieceCount * pieceHeight + p.Y * pieceSize), (int)pieceSize, (int)pieceSize), myGame.players[myGame.currentTurn].color);
                        else // draw 2nd half of pieces in right column
                            spriteBatch.Draw(texSquare, new Rectangle((int)(pieceLeft2 + p.X * pieceSize), (int)(Math.Floor(pieceCount - totalPieces / 2.0) * pieceHeight + p.Y * pieceSize), (int)pieceSize, (int)pieceSize), myGame.players[myGame.currentTurn].color);
                    }
                    pieceCount++;
                }
            }*/
            //draw available pieces
            int startTop = 200 - (int)scrollAvail;
            int availSize = (int)totalPanelSize / 5;
            int availCell = availSize / 6;
            int startLeft = availSize / 2;
            for (int ct = 0; ct < myGame.players[myGame.currentTurn].pieces.Count; ct++)
            {
                MyPiece piece = myGame.players[myGame.currentTurn].pieces[ct];
                if (piece.isAvailable) //only draw if available
                {
                    int col = pieceCount % 4;
                    int rw = pieceCount / 4;
                    if (pieceSelected == ct)
                        DrawSquareWithFill(startLeft + availSize * col, startTop + availSize * rw, 1, availSize + 1, availSize + 1, colorDark, colorLightest);
                    //DrawSquare(startLeft + availSize * col, startTop + availSize * rw, 1, availSize + 1, availSize + 1, Color.Black);
                    foreach (Point p in piece.points)
                    {
                        spriteBatch.Draw(texGamesquare, new Rectangle((int)(startLeft + availCell/2 + availSize*col + p.X * availCell), (int)(startTop + availCell/2 + rw * availSize + p.Y * availCell), (int)availCell, (int)availCell), myGame.players[myGame.currentTurn].color);
                    }
                    pieceCount++;
                }
            }
            DrawSquareWithFill(startLeft - 3, 200 - 3, 3, availSize * 4 + 6, (int)totalHeight - 200 - 6 - 15, colorDark, Color.Transparent);
            spriteBatch.Draw(texPixel, new Rectangle(0, 0, availSize * 4 + startLeft + 6, 197), colorMiddle);
            spriteBatch.Draw(texPixel, new Rectangle(0, 197 + (int)totalHeight - 200 - 6 - 15, availSize * 4 + startLeft + 6, 800-(197 + (int)totalHeight - 200 - 6 - 15)), colorMiddle);
        }

        public void DrawMouseCursor()
        {
            //draw mouse
            if (pieceSelected == -1 || Mouse.GetState().X < totalPanelSize) //draw plain cursor
            {
                blokusGame.IsMouseVisible = true;
                //spriteBatch.Draw(texPointer, new Rectangle(Mouse.GetState().X, Mouse.GetState().Y, 20, 20), Color.White);
            }
            else
            {
                blokusGame.IsMouseVisible = false;
                //a piece has been selected, so draw the piece -- this still needs to go back to plain when users change their minds
                MyPiece piece = myGame.players[myGame.currentTurn].pieces[pieceSelected];
                double ptLeft = -2.5 * gameCellWidth;
                double ptTop = -2.5 * gameCellHeight;
                foreach (Point p in piece.points)
                {
                    spriteBatch.Draw(texGamesquare, new Rectangle((int)(Mouse.GetState().X + ptLeft + p.X * gameCellWidth), (int)(Mouse.GetState().Y + ptTop + p.Y * gameCellHeight), (int)gameCellWidth, (int)gameCellHeight), myGame.players[myGame.currentTurn].color);
                }
            }
        }

        public override void ResetPrevKeys()
        {
            keyPrevState = Keyboard.GetState();
            gpPrevState = GamePad.GetState(PlayerIndex.One);
        }

#if (DEBUG)

        [Test]
        public void Test01_CheckPlacePiece()
        {
            newGame();
            myGame.PlacePiece(-4, 3, 0); //off the board
            Assert.AreEqual(0, myGame.currentTurn); //currentTurn should not change
            myGame.PlacePiece(10, 10, 0); //place in non-starting cell
            Assert.AreEqual(0, myGame.currentTurn); //currentTurn should not change
            myGame.PlacePiece(-2, -2, 0); //place in top-left corner
            Assert.AreEqual(1, myGame.currentTurn); //currentTurn SHOULD change
            Assert.IsTrue(myGame.boardMap[0, 0] >= 0); //piece has been placed in top left cell
            myGame.currentTurn--; //reset currentTurn to 0
            myGame.PlacePiece(-1, -2, 0); //piece has been placed next to old piece
            Assert.AreEqual(0, myGame.currentTurn);

            //check before and after 2nd piece placement
            Assert.IsTrue(myGame.boardMap[1, 1] == ApplicationLayer.Game.EMPTY);
            Assert.IsTrue(myGame.players[myGame.currentTurn].playerMap[1, 1] == Player.CORNER);
            myGame.PlacePiece(-1, -1, 0); //place to corner of placed piece
            Assert.IsTrue(myGame.boardMap[1, 1] >= 0);

        }

#endif

    }
}
